package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.binary.core.exception.BinaryException;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.dao.ArchReviewExpertConfDao;
import com.uinnova.product.eam.dao.ArchReviewExpertDetailConfDao;
import com.uinnova.product.eam.dto.SimpUserInfo;
import com.uinnova.product.eam.model.es.ArchReviewExpertConf;
import com.uinnova.product.eam.model.es.ArchReviewExpertDetailConf;
import com.uinnova.product.eam.service.ArchReviewExpertConfService;
import com.uinnova.product.eam.vo.ArchReviewExpertConfInfo;
import com.uinnova.product.eam.vo.ArchReviewExpertSaveVo;
import com.uinnova.product.eam.vo.GroupDomainExpert;
import com.uino.api.client.permission.IUserApiSvc;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.query.CSysUser;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
@Log4j2
public class ArchReviewExpertConfServiceImpl implements ArchReviewExpertConfService {

    @Autowired
    private ArchReviewExpertConfDao expertConfDao;
    @Autowired
    private ArchReviewExpertDetailConfDao expertDetailConfDao;
    @Autowired
    private IUserApiSvc userApiSvc;

    @Override
    public void saveExpertConf(List<ArchReviewExpertSaveVo> saveVos) {
        Assert.notEmpty(saveVos, "评审专家不能为空");
        Set<String> experts = new HashSet<>();
        List<String> groupA = new ArrayList<>();
        List<String> groupB = new ArrayList<>();
        for (ArchReviewExpertSaveVo saveVo : saveVos) {
            if (StringUtils.isBlank(saveVo.getDomain())) {
                throw new BinaryException("领域不能为空");
            }
            if (CollectionUtils.isEmpty(saveVo.getGroupAExperts())
                    && CollectionUtils.isEmpty(saveVo.getGroupBExperts())) {
                throw new BinaryException("【A/B】组至少配置一个领域专家名单");
            }
            if (!CollectionUtils.isEmpty(saveVo.getGroupAExperts())) {
                groupA.addAll(saveVo.getGroupAExperts());
                experts.addAll(saveVo.getGroupAExperts());
            }
            if (!CollectionUtils.isEmpty(saveVo.getGroupBExperts())) {
                groupB.addAll(saveVo.getGroupBExperts());
                experts.addAll(saveVo.getGroupBExperts());
            }
        }
        if (CollectionUtils.isEmpty(groupA) || CollectionUtils.isEmpty(groupB)) {
            String msg = CollectionUtils.isEmpty(groupA) ? "A" : "B";
            throw new BinaryException(msg.concat("组至少配置一个领域专家名单"));
        }
        List<SysUser> users = getUserByLoginCodes(experts);
        if (CollectionUtils.isEmpty(users) || experts.size() != users.size()) {
            throw new BinaryException("部分专家名单不存在，请刷新页面重试");
        }
        Map<String, SimpUserInfo> userInfoMap = users.stream().collect(Collectors.toMap(
                SysUser::getLoginCode, e -> new SimpUserInfo(e.getLoginCode(), e.getUserName())));
        Long archReviewConfId = ESUtil.getUUID();
        String curUserLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        ArchReviewExpertConf expertConf = new ArchReviewExpertConf();
        expertConf.setId(archReviewConfId);
        expertConf.setVersion(getMaxVersion() + 1);
        expertConf.setCreatorCode(curUserLoginCode);
        expertConf.setCreateTime(ESUtil.getNumberDateTime());
        expertConf.setModifierCode(curUserLoginCode);
        expertConf.setModifyTime(ESUtil.getNumberDateTime());

        List<ArchReviewExpertDetailConf> expertDetailConfs = new ArrayList<>();
        for (ArchReviewExpertSaveVo saveVo : saveVos) {
            ArchReviewExpertDetailConf expertDetailConf = new ArchReviewExpertDetailConf();
            expertDetailConf.setId(saveVo.getId() == null ? ESUtil.getUUID() : saveVo.getId());
            expertDetailConf.setExpertConfId(archReviewConfId);
            expertDetailConf.setDomain(saveVo.getDomain());
            expertDetailConf.setGroupAExperts(saveVo.getGroupAExperts());
            if (!CollectionUtils.isEmpty(saveVo.getGroupAExperts())) {
                List<SimpUserInfo> groupAExpertInfos = new ArrayList<>();
                saveVo.getGroupAExperts().forEach(groupAExpert -> {
                    groupAExpertInfos.add(userInfoMap.get(groupAExpert));
                });
                expertDetailConf.setGroupAExpertInfos(groupAExpertInfos);
            }
            expertDetailConf.setGroupBExperts(saveVo.getGroupBExperts());
            if (!CollectionUtils.isEmpty(saveVo.getGroupBExperts())) {
                List<SimpUserInfo> groupBExpertInfos = new ArrayList<>();
                saveVo.getGroupBExperts().forEach(groupBExpert -> {
                    groupBExpertInfos.add(userInfoMap.get(groupBExpert));
                });
                expertDetailConf.setGroupBExpertInfos(groupBExpertInfos);
            }
            if (saveVo.getId() == null) {
                expertDetailConf.setCreatorCode(curUserLoginCode);
                expertDetailConf.setCreateTime(ESUtil.getNumberDateTime());
            }
            expertDetailConf.setModifierCode(curUserLoginCode);
            expertDetailConf.setModifyTime(ESUtil.getNumberDateTime());
            expertDetailConfs.add(expertDetailConf);
        }
        expertConfDao.saveOrUpdate(expertConf);
        expertConf.setSnapShot(JSON.toJSONString(expertDetailConfs));
        Set<Long> existIds = saveVos.stream().map(ArchReviewExpertSaveVo::getId).filter(Objects::nonNull).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(existIds)) {
            TermsQueryBuilder deleteQuery = QueryBuilders.termsQuery("id", existIds);
            expertDetailConfDao.deleteByQuery(deleteQuery, true);
        }
        expertDetailConfDao.saveOrUpdateBatch(expertDetailConfs);
    }

    @Override
    public void checkPre(List<ArchReviewExpertSaveVo> saveVos) {
        Assert.notEmpty(saveVos, "评审专家不能为空");
        Set<String> experts = new HashSet<>();
        List<String> groupA = new ArrayList<>();
        List<String> groupB = new ArrayList<>();
        for (ArchReviewExpertSaveVo saveVo : saveVos) {
            if (StringUtils.isBlank(saveVo.getDomain())) {
                throw new BinaryException("领域不能为空");
            }
            if (CollectionUtils.isEmpty(saveVo.getGroupAExperts())
                    && CollectionUtils.isEmpty(saveVo.getGroupBExperts())) {
                throw new BinaryException("【A/B】组至少配置一个领域专家名单");
            }
            if (!CollectionUtils.isEmpty(saveVo.getGroupAExperts())) {
                groupA.addAll(saveVo.getGroupAExperts());
                experts.addAll(saveVo.getGroupAExperts());
            }
            if (!CollectionUtils.isEmpty(saveVo.getGroupBExperts())) {
                groupB.addAll(saveVo.getGroupBExperts());
                experts.addAll(saveVo.getGroupBExperts());
            }
        }
        if (CollectionUtils.isEmpty(groupA) || CollectionUtils.isEmpty(groupB)) {
            String msg = CollectionUtils.isEmpty(groupA) ? "A" : "B";
            throw new BinaryException(msg.concat("组至少配置一个领域专家名单"));
        }
        List<SysUser> users = getUserByLoginCodes(experts);
        if (CollectionUtils.isEmpty(users) || experts.size() != users.size()) {
            throw new BinaryException("部分专家名单不存在，请刷新页面重试");
        }
    }

    private Long getMaxVersion() {
        Page<ArchReviewExpertConf> page = expertConfDao.getSortListByQuery(1, 1, QueryBuilders.boolQuery(), "version", false);
        return CollectionUtils.isEmpty(page.getData()) ? 0L : page.getData().get(0).getVersion();
    }

    private List<SysUser> getUserByLoginCodes(Set<String> loginCodes) {
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodes(loginCodes.toArray(new String[loginCodes.size()]));
        return userApiSvc.getSysUserByCdt(cdt);
    }

    @Override
    public List<ArchReviewExpertConfInfo> expertConfInfo() {
        Long maxVersion = getMaxVersion();
        if (maxVersion == 0L) {
            return new ArrayList<>();
        }
        TermQueryBuilder expertConfQuery = QueryBuilders.termQuery("version", maxVersion);
        Page<ArchReviewExpertConf> page = expertConfDao.getSortListByQuery(
                1, 1, expertConfQuery, "createTime", false);
        ArchReviewExpertConf expertConf = page.getData().get(0);

        TermQueryBuilder expertDetailConfQuery = QueryBuilders.termQuery("expertConfId", expertConf.getId());
        Page<ArchReviewExpertDetailConf> detaiPage = expertDetailConfDao.getSortListByQuery(
                1, 3000, expertDetailConfQuery, "createTime", false);
        if (CollectionUtils.isEmpty(detaiPage.getData())) {
            return new ArrayList<>();
        }
        Set<String> experts = new HashSet<>();
        Map<String, SimpUserInfo> redundanceExpertMap = new ConcurrentHashMap<>();
        for (ArchReviewExpertDetailConf detailConf : detaiPage.getData()) {
            if (!CollectionUtils.isEmpty(detailConf.getGroupAExperts())) {
                experts.addAll(detailConf.getGroupAExperts());
                redundanceExpertMap.putAll(detailConf.getGroupAExpertInfos().stream()
                        .collect(Collectors.toMap(SimpUserInfo::getLoginCode, e -> e, (k1,k2) -> k1)));
            }
            if (!CollectionUtils.isEmpty(detailConf.getGroupBExperts())) {
                experts.addAll(detailConf.getGroupBExperts());
                redundanceExpertMap.putAll(detailConf.getGroupBExpertInfos().stream()
                        .collect(Collectors.toMap(SimpUserInfo::getLoginCode, e -> e, (k1,k2) -> k1)));
            }
        }
        Map<String, SimpUserInfo> dbExpertMap = new ConcurrentHashMap<>();
        List<SysUser> users = getUserByLoginCodes(experts);
        if (!CollectionUtils.isEmpty(users)) {
            dbExpertMap = users.stream().collect(Collectors.toMap(SysUser::getLoginCode,
                    e -> new SimpUserInfo(e.getLoginCode(), e.getUserName()), (k1,k2) -> k1));
        }
        List<ArchReviewExpertConfInfo> infos = new ArrayList<>();
        for (ArchReviewExpertDetailConf detailConf : detaiPage.getData()) {
            ArchReviewExpertConfInfo info = EamUtil.copy(detailConf, ArchReviewExpertConfInfo.class);
            List<SimpUserInfo> groupAExpertInfos = new ArrayList<>();
            if (!CollectionUtils.isEmpty(info.getGroupAExperts())) {
                for (String groupAExpert : info.getGroupAExperts()) {
                    String userName = dbExpertMap.getOrDefault(groupAExpert, redundanceExpertMap.get(groupAExpert)).getUserName();
                    groupAExpertInfos.add(new SimpUserInfo(groupAExpert, userName));
                }
            }
            info.setGroupAExpertInfos(groupAExpertInfos);
            List<SimpUserInfo> groupBExpertInfos = new ArrayList<>();
           if (!CollectionUtils.isEmpty(info.getGroupBExperts())) {
               for (String groupBExpert : info.getGroupBExperts()) {
                   String userName = dbExpertMap.getOrDefault(groupBExpert, redundanceExpertMap.get(groupBExpert)).getUserName();
                   groupBExpertInfos.add(new SimpUserInfo(groupBExpert, userName));
               }
           }
            info.setGroupBExpertInfos(groupBExpertInfos);
            infos.add(info);
        }
        return infos;
    }

    @Override
    public List<GroupDomainExpert> randomGroupABByDomain() {
        Long maxVersion = getMaxVersion();
        if (maxVersion == 0L) {
            throw new BinaryException("未配置预评审-初审专家");
        }
        TermQueryBuilder expertConfQuery = QueryBuilders.termQuery("version", maxVersion);
        Page<ArchReviewExpertConf> page = expertConfDao.getSortListByQuery(
                1, 1, expertConfQuery, "createTime", false);
        ArchReviewExpertConf expertConf = page.getData().get(0);
        TermQueryBuilder expertDetailConfQuery = QueryBuilders.termQuery("expertConfId", expertConf.getId());
        Page<ArchReviewExpertDetailConf> detaiPage = expertDetailConfDao.getSortListByQuery(
                1, 3000, expertDetailConfQuery, "createTime", false);
        int randomIndex = new Random().nextInt(2);
        log.info("随机到{}组专家", randomIndex == 0 ? "A" : "B");
        List<GroupDomainExpert> domainExperts = new ArrayList<>();
        for (ArchReviewExpertDetailConf detailConf : detaiPage.getData()) {
            if (randomIndex == 0) {
                if (CollectionUtils.isEmpty(detailConf.getGroupAExperts())) {
                    log.info("领域[{}]内A组无评审名单，跳过当前领域", detailConf.getDomain());
                    continue;
                }
                domainExperts.add(new GroupDomainExpert("A组", detailConf.getId(), detailConf.getDomain(), detailConf.getGroupAExperts()));
                continue;
            }
            if (CollectionUtils.isEmpty(detailConf.getGroupBExperts())) {
                log.info("领域[{}]内B组无评审名单，跳过当前领域", detailConf.getDomain());
                continue;
            }
            domainExperts.add(new GroupDomainExpert("B组", detailConf.getId(), detailConf.getDomain(), detailConf.getGroupBExperts()));
        }
        if (CollectionUtils.isEmpty(domainExperts)) {
            String msg = "所有领域内".concat(randomIndex == 0 ? "A" : "B").concat("组评审名单都为空");
            log.info(msg);
            log.info("detailConfs:{}", JSON.toJSONString(detaiPage.getData()));
            throw new BinaryException(msg);
        }
        log.info("专家信息:{}", JSON.toJSONString(domainExperts));
        return domainExperts;
    }
}
